/**
 * @file wa_wm.c
 *
 */

 /*********************
  *      INCLUDES
  *********************/
#include "wa_wm.h"
#include "wa_mask_draw.h"
#include "wa_screen_erase.h"
/*********************
*      DEFINES
*********************/

/**********************
*      TYPEDEFS
**********************/

/**********************
*  STATIC PROTOTYPES
**********************/
static void wa_wm_context_init(void);
static void wa_wm_redraw(void);
/**********************
*  STATIC VARIABLES
**********************/
wa_wm_context wm_context;
/**********************
*      MACROS
**********************/

/**********************
*   GLOBAL FUNCTIONS
**********************/

void wa_wm_module_init(void)
{
	wa_wm_context_init();
}

void wa_wm_handle(void)
{
	wa_wm_redraw();
}

/**
 * update the redraw area rect
 * @param area which area will be handle
 */
void wa_wm_update_redraw_rect(wa_area* area)
{
	if (wm_context.redraw_rect_have == wa_nok)
	{
		wa_area_copy(&(wm_context.redraw_rect), area);
		wm_context.redraw_rect_have = wa_ok;
	}
	else
	{
		wa_area_join(&(wm_context.redraw_rect), &(wm_context.redraw_rect), area);
	}
}

/**
 * send sign to obj
 * @param obj
 * @param sign
 */
void wa_wm_send_sign(wa_obj* obj, wa_sign* sign)
{
	if (obj != NULL)
	{
		sign->obj_dst = obj;
		obj->cb(sign);
	}
}

/**
 * send sign to obj, but no data
 * @param obj
 * @param sign_id
 */
void wa_wm_send_sign_nodata(wa_obj* obj, int sign_id)
{
	if (obj != NULL)
	{
		wa_sign sign;
		sign.obj_dst = obj;
		sign.sign_id = sign_id;
		wa_wm_send_sign(obj, &sign);
	}
}

/**
 * send sign that notify parent
 * @param obj
 * @param sign_id
 */
void wa_wm_notify_parent(wa_obj* obj, int sign_id)
{
	if (obj != NULL)
	{
		if (obj->par != NULL)
		{
			wa_sign sign;
			sign.obj_src = obj;
			sign.obj_dst = obj->par;
			sign.sign_id = WA_SIGNAL_NOTIFY_PARENT;
			sign.Data.v = sign_id;
			wa_wm_send_sign(obj->par, &sign);
		}
	}
}

/**
 * pid handle
 */
void wa_wm_pid_handle(void)
{
	wa_pid_state pid_state;
	wa_pid_get_state(&pid_state);
	wa_pid_proc_point(&pid_state);
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

 /**
  * context struct init
  */
static void wa_wm_context_init(void)
{
	wm_context.redraw_rect.x1 = 0;
	wm_context.redraw_rect.y1 = 0;
	wm_context.redraw_rect.x2 = 0;
	wm_context.redraw_rect.y2 = 0;
	wm_context.redraw_rect_have = wa_nok;
	wm_context.obj_drawing = NULL;
}

/**
 * start in obj, find the lowest child whick include all mask
 * @param obj start obj
 * @param mask_p
 * @return which obj can be find
 */
static wa_obj* wa_wm_radraw_find_top_obj(wa_obj* obj, wa_area* mask_p)
{
	wa_obj* obj_found = NULL;
	wa_obj* child;
	if ((wa_area_is_in(mask_p, &(obj->rect)) == wa_ok) && (obj->flag & WA_CF_SHOW))
	{
		for (child = wa_ll_get_head(&(obj->child_ll)); child != NULL; child = wa_ll_get_next(&(obj->child_ll), child))
		{
			obj_found = wa_wm_radraw_find_top_obj(child, mask_p);
			if (obj_found != NULL)
			{
				break;
			}
		}
		if (obj_found == NULL)
		{
			obj_found = obj;
		}
	}
	return obj_found;
}

/**
 * redram a obj and all it's child obj
 * @param obj
 * @param mask_p
 */
static void wa_wm_redraw_obj_and_child(wa_obj* obj, wa_area* mask_p)
{
	wa_area obj_mask;
	wa_result union_ok;
	wa_obj* child;
	if ((obj->flag & WA_CF_SHOW) == 0)
	{
		return;
	}
	union_ok = wa_area_union(&obj_mask, mask_p, &(obj->rect));
	if (union_ok == wa_ok)
	{
		wa_area_copy(&(obj->redraw_rect), &obj_mask);
		wm_context.obj_drawing = obj;
		wa_wm_send_sign_nodata(obj, WA_SIGNAL_PAINT);

		for (child = wa_ll_get_tail(&(obj->child_ll)); child != NULL; child = wa_ll_get_prev(&(obj->child_ll), child))
		{
			wa_area child_mask;
			union_ok = wa_area_union(&child_mask, &obj_mask, &(child->rect));
			if (union_ok == wa_ok)
			{
				wa_wm_redraw_obj_and_child(child, &child_mask);
			}
		}
	}
}

/**
 * redraw all obj
 */
static void wa_wm_redraw(void)
{
	wa_obj* obj_top;
	wa_obj* par;
	wa_obj* brother;
	wa_obj* now;
	int w, h;

	if (wm_context.redraw_rect_have == wa_nok)
	{
		return;
	}

	obj_top = wa_wm_radraw_find_top_obj(&top_scr_obj, &(wm_context.redraw_rect));
	if (obj_top == NULL)
	{
		obj_top = &top_scr_obj;
	}

	wa_wm_redraw_obj_and_child(obj_top, &(wm_context.redraw_rect));

	par = wa_obj_get_parent(obj_top);
	now = obj_top;
	while (par != NULL)
	{
		brother = wa_ll_get_prev(&(par->child_ll), now);
		while (brother != NULL)
		{
			wa_wm_redraw_obj_and_child(brother, &(wm_context.redraw_rect));
			brother = wa_ll_get_prev(&(par->child_ll), brother);
		}
		now = par;
		par = wa_obj_get_parent(par);
	}
	
	wa_screen_erase_handle();

	w = wa_area_get_width(&(wm_context.redraw_rect));
	h = wa_area_get_height(&(wm_context.redraw_rect));
	wa_mask_update(wm_context.redraw_rect.x1, wm_context.redraw_rect.y1, w, h);

	wm_context.redraw_rect_have = wa_nok;
	wm_context.obj_drawing = NULL;
}

